package com.jumei.flume.parser;

import org.joda.time.DateTime;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * 针对一个日志输入源当中的日志行对应种Category的情况，我们可以继承该抽象类进行处理。
 * 其封装了关于SubParser的注册及调用机制等。
 */
public abstract class AbstractMultiCategoryLogLineParser extends AbstractLogLineParser {
    private static final Logger logger = LoggerFactory.getLogger(AbstractMultiCategoryLogLineParser.class);
    /**
     * 所有注册的SubParser的Map
     */
    private Map<String, LogLineSubParser> categorySubParserMap = new HashMap<String, LogLineSubParser>();

    /**
     * 所有注册的基于categoryFlag的SubParser的HashMap
     */
    private Map<String, LogLineParser> categoryFlagSubParserMap = new HashMap<String, LogLineParser>();
    private Map<String, String> categoryFlagCategoryMap = new HashMap<String, String>();

    private List<String> ignoreCategories;

    /**
     * 注册同一个日志下各种Category的SubParser
     *
     * @param logLineSubParser
     */
    protected void registerSubParser(LogLineSubParser logLineSubParser) throws Exception {
        if (logLineSubParser != null) {
            String category = logLineSubParser.getCategory();
            String[] categoryFlags = logLineSubParser.getCategoryFlag();
            if (categorySubParserMap.containsKey(category)) {
                String errmsg = "when registering SubParser[" + logLineSubParser.getClass().getName()
                        + "] to " + this.getClass().getName()
                        + ",found already registered category[" + category + "] for SubParser["
                        + categorySubParserMap.get(category).getClass().getName() + "]";

                throw new Exception(errmsg);
            }

            for (String categoryFlag : categoryFlags) {

                if (categoryFlagSubParserMap.containsKey(categoryFlag)) {
                    String errmsg = "when registering SubParser[" + logLineSubParser.getClass().getName()
                            + "] to " + this.getClass().getName()
                            + ",found already registered categoryFlag[" + categoryFlag + "] for SubParser["
                            + categoryFlagSubParserMap.get(categoryFlag).getClass().getName() + "]";

                    throw new Exception(errmsg);
                }
                //categorySubParserMap.put(categoryFlag, logLineSubParser);
                categoryFlagCategoryMap.put(categoryFlag, logLineSubParser.getCategory());
            }

            categorySubParserMap.put(logLineSubParser.getCategory(), logLineSubParser);

            logLineSubParser.register(this);
        }
    }

    /**
     * 使用匹配的SubParser来解析
     *
     * @param categoryString
     * @param line
     * @param splitedValues
     * @return
     */
    protected List<ParsedLine> doSubParserParse(String categoryString, String line, String[] splitedValues) {
        List<ParsedLine> parsedLines = null;
        for (Map.Entry<String, LogLineSubParser> entry : categorySubParserMap.entrySet()) {
            String category = entry.getKey();
            LogLineSubParser subParser = entry.getValue();
            String[] categoryFlags = subParser.getCategoryFlag();
            for (String categoryFlag : categoryFlags) {
                if (categoryString.indexOf(categoryFlag) > -1) {
                    if (ignoreCategories != null && ignoreCategories.contains(category)) {
                        break;
                    }
                    try {
                        parsedLines = subParser.parse(line, splitedValues);
                    } catch (Exception e) {
                        logger.error("use " + subParser.getCategory() + " Parser parse line failed!", e);
                    }
                    break;
                }
            }
        }
        return parsedLines;
    }

    /**
     * 设置需要忽略的Category
     *
     * @param categories
     */
    public void setIgnoreCategories(String... categories) {
        this.ignoreCategories = Arrays.asList(categories);
    }

    /**
     * 解析日志行的创建时间
     * 注：假定针对同一个日志，大多数子Category的日志行创建时间都是一致的，那么在SubParser通过引用调用该方法就比较方便。
     * 当某个Category的日志行创建时间的获取和其不一致，请自行实现
     *
     * @param line
     * @param splitedValues
     * @return
     */
    abstract public DateTime parseCreateTime(String line, String[] splitedValues);
}
